namespace SolidMvvm {
	using System;
	using System.Diagnostics;
	using System.IO;
	using System.IO.Packaging;
	using System.Net.Mime;

	public class LoggerZip : LoggerBase {
		Package package;

		PackagePart packagePart;

		Stream stream;

		StreamWriter streamWriter;

		public LoggerZip(string filePathWithFileExtension) : base(filePathWithFileExtension) {
			try {
				var nowInUniversalTime = DateTime.UtcNow;
				var logFileName = GetLogFilePath(nowInUniversalTime);

				try {
					using (Package.Open(logFileName)) {}
				}
				catch {
					File.Move(logFileName, logFileName + "_" + DateTime.Now.ToFileTime());
				}

				package = Package.Open(logFileName);

				var packagePartUri = PackUriHelper.CreatePartUri(new Uri(Path.GetFileName(logFileName), UriKind.Relative));
				packagePart = null;
				if (!package.PartExists(packagePartUri))
					packagePart = package.CreatePart(packagePartUri, MediaTypeNames.Text.Plain, CompressionOption.Maximum);
				else
					packagePart = package.GetPart(packagePartUri);

				if (packagePart == null)
					throw new GeneralException("Unable to create log file.", null);

				stream = packagePart.GetStream(FileMode.OpenOrCreate);
				stream.Seek(0, SeekOrigin.End);
				streamWriter = new StreamWriter(stream);

				AddMessage("Log initialized.", null); // Ensures the log file is working.
			}
			catch (Exception exception) {
				Cleaner.CleanConstructor(this, exception);
			}
		}

		public override void Dispose() {
			using (var cleaner = new Cleaner()) {
				cleaner.CleanWithLambda(() => {
					if (FilePathWithFileExtension != null && streamWriter != null)
						AddMessage("Log finalized.", null);
				});

				cleaner.DisposeObject(ref streamWriter);

				cleaner.DisposeObject(ref stream);

				cleaner.DisposeObject(ref package);

				packagePart = null;

				cleaner.CleanWithLambda(() => base.Dispose());
			}
		}

		public override string GetLogFileAsString() {
			streamWriter.Close();
			streamWriter = null;
			stream.Close();

			stream = packagePart.GetStream(FileMode.OpenOrCreate);
			stream.Seek(0, SeekOrigin.Begin);

			string logFileAsString;
			using (var temporaryStreamReader = new StreamReader(stream))
				logFileAsString = temporaryStreamReader.ReadToEnd();
			stream.Close();

			stream = packagePart.GetStream(FileMode.OpenOrCreate);
			stream.Seek(0, SeekOrigin.End);
			streamWriter = new StreamWriter(stream);

			return logFileAsString;
		}

		public override void AddMessage(string publicMessage, string privateMessageOrNull) {
			// Date + Time + Entry Type + Message. Log to file and to console (in Debug mode).
			try {
				var dateTimeStamp = DateTime.Now.ToString("O");

				// Write log entry as comma-delimited.
				var logMessage = dateTimeStamp + ", \"" + GetCSVedString(publicMessage) + "\", \"" + (privateMessageOrNull == null ? string.Empty : GetCSVedString(privateMessageOrNull)) + "\"";

				streamWriter.WriteLine(logMessage);
			}
			catch (Exception exception) {
				Trace.WriteLine("Error writing to log:\n\n" + exception);
			}
		}
	}
}
